package com.zhanggod.jcheck.utils.redis;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

/**
 * @author syxl
 * @date
 */
@Slf4j
public class RedisLock {

    private RedisBase redisBase;

    /**
     * 重试时间
     */
    private static final int DEFAULT_RETRY_MILLIS = 100;

    /**
     * 重试时间
     */
    private static final int DEFAULT_EXPIRE_SECONDS = 60;

    /**
     * 锁的后缀
     */
    private static final String LOCK_SUFFIX = "redis_lock";

    /**
     * 锁的后缀
     */
    private static final int LOCK_TIMEOUT_JUDGE_DEFAULT_ZERO_VALUE = 0;

    /**
     * 锁的后缀
     */
    private static final int LOCK_EXPIRE_ADD_DEFAULT_ONE_VALUE = 1;

    /**
     * 锁的key
     */
    private String lockKey;
    /**
     * 锁超时时间，防止线程在入锁以后，防止阻塞后面的线程无法获取锁毫秒
     */
    private int expireMillisecond = 60 * 1000;

    /**
     * 线程获取锁的等待时间
     */
    private int timeoutMillisecond = 9 * 1000;

    /**
     * 是否锁定标志
     */
    private volatile boolean locked = false;

    /**
     * 构造器
     *
     * @param lockKey 锁的key
     */
    public RedisLock(String lockKey, RedisBase redisBase) {
        this.expireMillisecond = 600;
        this.timeoutMillisecond = 9000;
        this.locked = false;
        this.lockKey = lockKey + ":" + "redis_lock";
        this.redisBase = redisBase;
    }

    public RedisLock(String lockKey, RedisBase redisBase, String suffix) {
        this.lockKey = lockKey + suffix;
        this.redisBase = redisBase;
    }

    /**
     * 构造器
     *
     * @param lockKey            锁的key
     * @param timeoutMillisecond 获取锁的超时时间
     */
    public RedisLock(String lockKey, int timeoutMillisecond, RedisBase redisBase) {
        this(lockKey, redisBase);
        this.timeoutMillisecond = timeoutMillisecond;
    }

    /**
     * 构造器
     *
     * @param lockKey            锁的key
     * @param timeoutMillisecond 获取锁的超时时间
     * @param expireMillisecond  锁的有效期
     */
    public RedisLock(String lockKey, int timeoutMillisecond, int expireMillisecond, RedisBase redisBase) {
        this(lockKey, timeoutMillisecond, redisBase);
        this.expireMillisecond = expireMillisecond;
    }

    /**
     * 获取锁
     *
     * @return 获取锁成功返回true，超时返回false
     * @throws InterruptedException
     */
    public boolean lock() throws InterruptedException {
        int timeout = this.timeoutMillisecond;

        while(timeout >= 0) {
            log.info("连点进行中1的timeout：{}", timeout);
            long expires = System.currentTimeMillis() + (long)this.expireMillisecond + 1L;
            String expiresStr = String.valueOf(expires);
            if (this.redisBase.setIfAbsent(this.lockKey, expiresStr)) {
                this.redisBase.expire(this.lockKey, 60L, TimeUnit.SECONDS);
                this.locked = true;
                return true;
            }

            String currentValue = this.redisBase.get(this.lockKey);
            if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {
                String oldValue = this.redisBase.getAndSet(this.lockKey, expiresStr);
                if (oldValue != null && oldValue.equals(currentValue)) {
                    this.locked = true;
                    return true;
                }
            }

            timeout -= 100;
            Thread.sleep(100L);
        }

        log.info("连点可以走到最后");
        return false;
    }

    /**
     * 释放获取到的锁
     */
    public synchronized void unlock() {
        if (locked) {
            redisBase.delete(lockKey);
            locked = false;
        }
    }
}
